package com.bianguo.android.eventbuslibrary;

import android.app.Application;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkRequest;
import android.os.Build;
import android.text.TextUtils;

import com.bianguo.android.eventbuslibrary.annotation.Network;
import com.bianguo.android.eventbuslibrary.bean.EventBean;
import com.bianguo.android.eventbuslibrary.bean.NetworkBean;
import com.bianguo.android.eventbuslibrary.enums.NetworkState;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

public class NetworkManager {

    private static volatile NetworkManager instance;
    private HashMap<Object, ArrayList<NetworkBean>> stateMap;
    private Application application;

    private NetworkManager() {
        stateMap = new HashMap<>();
    }

    public static NetworkManager getDefault() {
        NetworkManager manager = instance;
        if (manager == null) {
            synchronized (NetworkManager.class) {
                if (manager == null) {
                    manager = new NetworkManager();
                    instance = manager;
                }
            }
        }
        return manager;
    }


    public void register(Object target) {
        if (!isRegister(target)) {
            stateMap.put(target, subscribe(target));
        }
    }

    public void unRegister(Object target) {
        if (!stateMap.isEmpty()) {
            stateMap.remove(target);
        }
    }

    public void unRegisterAll() {
        if (!stateMap.isEmpty()) {
            stateMap.clear();
        }
    }

    private ArrayList<NetworkBean> subscribe(Object target) {
        ArrayList<NetworkBean> networkBeans = new ArrayList<>();
        Class<?> clazz = target.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            Network network = method.getAnnotation(Network.class);
            if (network != null) {
                Class<?> returnType = method.getReturnType();
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (TextUtils.equals("void", returnType.toString()) && parameterTypes.length == 1) {
                    NetworkBean networkBean = new NetworkBean();
                    networkBean.setMethod(method);
                    networkBean.setParamType(parameterTypes[0]);
                    networkBean.setNetworkState(network.state());
                    networkBeans.add(networkBean);
                }
            }
        }
        return networkBeans;
    }

    public void post(NetworkState networkState) {
        Iterator<Object> iterator = stateMap.keySet().iterator();
        while (iterator.hasNext()) {
            Object key = iterator.next();
            ArrayList<NetworkBean> networkBeans = stateMap.get(key);
            if (networkBeans != null) {
                for (NetworkBean networkBean : networkBeans) {
                    if (networkBean.getParamType().isAssignableFrom(networkState.getClass())) {
                        switch (networkBean.getNetworkState()) {
                            case AUTO:
                                invoke(networkBean, key, networkState);
                                break;
                            case WIFI:
                                if (networkState == NetworkState.WIFI || networkState == NetworkState.NONE) {
                                    invoke(networkBean, key, networkState);
                                }
                                break;
                            case CMWAP:
                                if (networkState == NetworkState.CMWAP || networkState == NetworkState.NONE) {
                                    invoke(networkBean, key, networkState);
                                }
                                break;
                            case CMNET:
                                if (networkState == NetworkState.CMNET || networkState == NetworkState.NONE) {
                                    invoke(networkBean, key, networkState);
                                }
                                break;
                        }
                    }
                }
            }
        }
    }

    private void invoke(NetworkBean networkBean, Object target, NetworkState networkState) {
        Method method = networkBean.getMethod();
        try {
            method.invoke(target, networkState);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public boolean isRegister(Object target) {
        return stateMap.containsKey(target);
    }

    public void init(Application application) {
        if (application == null) {
            return;
        }
        this.application = application;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ConnectivityManager.NetworkCallback networkCallback = new NetworkCallBack();
            ConnectivityManager manager = (ConnectivityManager) application.getSystemService(Context.CONNECTIVITY_SERVICE);
            if (manager != null) {
                manager.requestNetwork(new NetworkRequest.Builder().build(), networkCallback);
            }
        }
    }
}
